﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using BP.DA;

namespace CCFlow.NetCore.DataUser.SocketImpl
{
    public class SharedEditor
    {
        private static readonly List<RoomInfo> RoomList = new(); // 存储连接的 WebSocket 客户端

        // 推送数据
        private async Task PubData(WebSocket ws, RoomInfo room)
        {
            await SendMsgToUser(new MessageBody
            {
                Type = MessageType.PubData,
                Data = room.SharedData,
            }, ws);
        }

        private async Task UpdateOnlineUsers(RoomInfo room)
        {
            await BroadcastToRoomUsers(new MessageBody
            {
                Type = MessageType.UpdateOnlineUsers,
                Data = new Dictionary<string, object>
                {
                    ["users"] = room.OnlineUsers.Select(user =>
                        new
                        {
                            user.UserId,
                            user.Username,
                            user.HexColor,
                            user.VoteStatus,
                            user.IsVoteStarter,
                        }).ToList()
                }
            }, room.Id);
        }

        // 登录
        private async Task Login(WebSocket ws, string token, string roomId)
        {
            try
            {
                var dt = DBAccess.RunSQLReturnTable($"SELECT No,Name FROM WF_Emp WHERE AtPara LIKE '%{token}%'");
                var userName = dt.Rows[0]["Name"].ToString();
                var userNo = dt.Rows[0]["No"].ToString();
                var u = new OnlineUser
                {
                    UserId = userNo,
                    Username = userName,
                    Socket = ws,
                    Token = token,
                    HexColor = Utils.GetRandomHexColor()
                };
                var r = RoomList.Find(room => room.Id == roomId);
                if (r != null)
                {
                    if (r.OnlineUsers.Find(onlineUser => onlineUser.UserId == userNo) == null)
                    {
                        r.OnlineUsers.Add(u);
                    }
                }
                else
                {
                    r = new RoomInfo { Id = roomId, OnlineUsers = new List<OnlineUser> { u } };
                    RoomList.Add(r);
                }

                await PubData(ws, r);
                await UpdateOnlineUsers(r);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                // 鉴权失败，关闭链接
                await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
            }
        }

        // 退出
        private async Task Logout(WebSocket ws, string token, string roomId)
        {
            var r = RoomList.Find(room => room.Id == roomId);
            if (r == null) return;
            r.OnlineUsers.RemoveAll(u => u.Token == token);
            await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing",
                CancellationToken.None);
            await UpdateOnlineUsers(r);
        }

        // 发起投票
        private async Task StartVote(RoomInfo room)
        {
            await BroadcastToRoomUsers(new MessageBody
            {
                Type = MessageType.StartVote,
                Data = new Dictionary<string, object>
                {
                    ["Status"] = room.RoomVoteInfo.Status,
                    ["ExpiredTimeStamp"] = room.RoomVoteInfo.ExpiredTimeStamp,
                }
            }, room.Id);
            await UpdateOnlineUsers(room);
        }

        // 更新投票信息
        private async Task UpdateVoteStatus(RoomInfo room) {
            bool allAgree = true;
            foreach (var user in room.OnlineUsers) {
                if (user.VoteStatus != UserVoteStatus.Agree) {
                    allAgree = false;
                    break;
                }
            }
            if (allAgree) {
                room.SharedData.Clear();
                await VoteEnd(room, "agree");
                // 投票成功，移除当前房间信息
                RoomList.Remove(room);
            } else {
                await UpdateOnlineUsers(room);
            }
        }
        private async Task VoteEnd(RoomInfo room, string status) {
            // 结束投票，投票失败
            room.RoomVoteInfo.Status = RoomVoteStatus.Pending;
            room.RoomVoteInfo.ExpiredTimeStamp = 0;
            room.OnlineUsers.ForEach(user => user.VoteStatus = UserVoteStatus.None);
            await BroadcastToRoomUsers(new MessageBody
            {
                Type = MessageType.UpdateVoteList,
                Data = new Dictionary<string, object>
                {
                    ["Status"] = status,
                }
            }, room.Id);
            await UpdateOnlineUsers(room);
        }
        // 更新共享数据
        private async Task UpdateRoomData(string roomId, string key, object value)
        {
            var room = RoomList.Find(room => room.Id == roomId);
            if (room == null) return;
            room.SharedData[key] = value;
            await BroadcastToRoomUsers(new MessageBody
            {
                Data = new Dictionary<string, object>
                {
                    [key] = value
                },
                Type = MessageType.UpdateFieldValue
            }, roomId);
        }

        public async Task HandleWebSocketConnection(WebSocket webSocket)
        {
            var buffer = new byte[1024 * 30]; // 用于接收消息的缓冲区 30KB
            try
            {
                string roomId = null;
                string token = null;
                // 持续接收消息
                while (webSocket.State == WebSocketState.Open)
                {
                    var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                    switch (result.MessageType)
                    {
                        case WebSocketMessageType.Text:
                            {
                                var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                                var msg = JsonSerializer.Deserialize<MessageBody>(message);
                                roomId = msg.Data["roomId"].ToString();
                                token = msg.Data["token"].ToString();
                                if (roomId == null)
                                {
                                    await webSocket.CloseAsync(WebSocketCloseStatus.InvalidPayloadData, "Closing",
                                        CancellationToken.None);
                                    return;
                                }

                                switch (msg.Type)
                                {
                                    case MessageType.Login:
                                        await Login(webSocket, token, roomId);
                                        break;
                                    case MessageType.Exit:
                                        await Logout(webSocket, token, roomId);
                                        break;
                                    case MessageType.LockField:
                                    case MessageType.ReleaseField:
                                        {
                                            string field = msg.Data["field"].ToString();
                                            if (field == null) return;
                                            var room = RoomList.Find(room => room.Id == roomId);
                                            var user = room?.OnlineUsers
                                                .Find(onlineUser => onlineUser.Token == token);
                                            if (user == null) return;
                                            if (msg.Type == MessageType.LockField)
                                            {
                                                room.EditInfo[field] = user.UserId;
                                            }
                                            else
                                            {
                                                room.EditInfo.Remove(field);
                                            }

                                            await BroadcastToRoomUsers(new MessageBody
                                            {
                                                Data = new Dictionary<string, object>
                                                {
                                                    ["EditInfo"] = room.EditInfo,
                                                },
                                                Type = MessageType.UpdateEditInfo
                                            }, roomId);
                                            break;
                                        }
                                    case MessageType.StartVote:
                                        {
                                            var room = RoomList.Find(room => room.Id == roomId);
                                            if (room == null) return;
                                            var user = room.OnlineUsers.Find(onlineUser => onlineUser.Token == token);
                                            if (user == null) return;
                                            room.OnlineUsers.ForEach(u => u.IsVoteStarter = false);
                                            user.IsVoteStarter = true;
                                            // 发起人必定同意提交
                                            user.VoteStatus = UserVoteStatus.Agree;
                                            
                                            room.RoomVoteInfo.Status = RoomVoteStatus.InProgress;
                                            room.RoomVoteInfo.ExpiredTimeStamp = DateTimeOffset.Now.AddMilliseconds(30 * 1000).ToUnixTimeMilliseconds();
                                            await StartVote(room);
                                            break;
                                        }

                                    case MessageType.AgreeSubmit:
                                    {
                                        var room = RoomList.Find(room => room.Id == roomId);
                                        if (room == null) return;
                                        var user = room.OnlineUsers.Find(onlineUser => onlineUser.Token == token);
                                        if (user == null) return;
                                        user.VoteStatus = UserVoteStatus.Agree;
                                        await UpdateVoteStatus(room);
                                        break;
                                    }
                                    case MessageType.DenySubmit:
                                    {
                                        var room = RoomList.Find(room => room.Id == roomId);
                                        if (room == null) return;
                                        var user = room.OnlineUsers.Find(onlineUser => onlineUser.Token == token);
                                        if (user == null) return;
                                        user.VoteStatus = UserVoteStatus.Reject;
                                        await VoteEnd(room, "reject");
                                        break;
                                    }
                                    case MessageType.Ping:
                                        await SendMsgToUser(new MessageBody
                                        {
                                            Type = MessageType.Ping,
                                            Data = null
                                        }, webSocket);
                                        break;
                                    case MessageType.UpdateFieldValue:
                                        await UpdateRoomData(roomId, msg.Data["key"].ToString(),
                                            msg.Data["value"]);
                                        break;
                                }

                                break;
                            }

                        case WebSocketMessageType.Close:
                            if (token != null)
                            {
                                await Logout(webSocket, token, roomId);
                            }

                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(@"WebSocket error: " + ex.Message);
            }
        }

        private async Task SendMsgToUser(MessageBody message, WebSocket ws)
        {
            var json = JsonSerializer.Serialize(message);
            // 广播消息到所有连接的客户端
            var messageBuffer = Encoding.UTF8.GetBytes(json);
            var segment = new ArraySegment<byte>(messageBuffer);
            await ws.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None);
        }

        private async Task BroadcastToRoomUsers(MessageBody message, string roomId)
        {
            var json = JsonSerializer.Serialize(message);
            // 广播消息到所有连接的客户端
            var messageBuffer = Encoding.UTF8.GetBytes(json);
            var segment = new ArraySegment<byte>(messageBuffer);
            var roomInfo = RoomList.Find(room => room.Id == roomId);
            if (roomInfo == null) return;
            foreach (var user in roomInfo.OnlineUsers.Where(user => user.Socket.State == WebSocketState.Open))
            {
                await user.Socket.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None);
            }
        }
    }
}